// bram控制器与ecc控制器 (8-bit BRAM version with packet header skipping)
(* DONT_TOUCH = "TRUE" *)
module bram_ecc_control #(
    parameter mux_num = 1536,
    parameter bram_addr_width = 12, // For mux_num=1536, addr_width >= 11. 12 is safe.
    parameter error_cnt_threshold = 255,
    parameter packet_header = 4
)(
    input clk_200m,
    input rst_n,

    // spad信号
    input clk_tx,
    input cnt_vld,
    input [7:0] pixel_in, // spad输入像素数据

    // bram 写入信号
    output reg [bram_addr_width - 1 : 0] datin_bram_w_addr,
    output reg [7:0] datin_bram_w_data,
    output reg datin_bram_w_en,
    output reg datin_bram_w_we,

    // 状态信号
    output reg busy,
    output reg [7:0] error_cnt,
    output reg error_rst
);

//================================================================
// 内部信号定义
//================================================================

// 状态机状态定义
localparam IDLE        = 3'b001;
localparam SAMPLING    = 3'b010;
localparam FINISH      = 3'b100;
localparam ERROR_RESET = 3'b101;

reg [2:0] current_state, next_state;

// 用于下降沿检测的同步寄存器
reg clk_tx_d0, clk_tx_d1;
wire clk_tx_negedge;

// 用于cnt_vld边沿检测
reg cnt_vld_d0, cnt_vld_d1;
wire cnt_vld_posedge;
wire cnt_vld_negedge;

// 数据采集计数器 (需要能计数到 mux_num + packet_header)
localparam TOTAL_SAMPLES = mux_num + packet_header;
reg [$clog2(TOTAL_SAMPLES):0] sample_cnt; // 记录采集了多少个8bit数据(包括包头)

//================================================================
// 边沿检测逻辑 (在clk_200m时钟域)
//================================================================
assign clk_tx_negedge = ~clk_tx_d0 & clk_tx_d1;
assign cnt_vld_posedge = cnt_vld_d0 & ~cnt_vld_d1;
assign cnt_vld_negedge = ~cnt_vld_d0 & cnt_vld_d1;

always @(posedge clk_200m or negedge rst_n) begin
    if (!rst_n) begin
        clk_tx_d0  <= 1'b0;
        clk_tx_d1  <= 1'b0;
        cnt_vld_d0 <= 1'b0;
        cnt_vld_d1 <= 1'b0;
    end else begin
        clk_tx_d0  <= clk_tx;
        clk_tx_d1  <= clk_tx_d0;
        cnt_vld_d0 <= cnt_vld;
        cnt_vld_d1 <= cnt_vld_d0;
    end
end

//================================================================
// 状态机逻辑
//================================================================

// 状态机时序逻辑
always @(posedge clk_200m or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= IDLE;
    end else begin
        current_state <= next_state;
    end
end

// 状态机组合逻辑
always @(*) begin
    next_state = current_state;
    case (current_state)
        IDLE: begin
            if (cnt_vld_posedge) begin
                next_state = SAMPLING;
            end
        end
        SAMPLING: begin
            if (cnt_vld_negedge) begin
                next_state = FINISH;
            end
        end
        FINISH: begin
             next_state = IDLE;
             if (error_cnt >= error_cnt_threshold) begin
                 next_state = ERROR_RESET;
             end
        end
        ERROR_RESET: begin
            next_state = IDLE;
        end
        default: begin
            next_state = IDLE;
        end
    endcase
end


//================================================================
// 主要控制及数据处理逻辑
//================================================================

always @(posedge clk_200m or negedge rst_n) begin
    if (!rst_n) begin
        // 复位所有输出和内部寄存器
        datin_bram_w_addr <= 0;
        datin_bram_w_data <= 0;
        datin_bram_w_en   <= 1'b0;
        datin_bram_w_we   <= 1'b0;
        busy              <= 1'b0;
        error_cnt         <= 8'd0;
        error_rst         <= 1'b0;
        sample_cnt        <= 0;
    end else begin
        // 默认情况下，写使能为低，error_rst只持续一个周期
        datin_bram_w_en <= 1'b0;
        datin_bram_w_we <= 1'b0;
        error_rst       <= 1'b0;

        case (current_state)
            IDLE: begin
                busy <= 1'b0;
                if (next_state == SAMPLING) begin // 当即将进入SAMPLING状态时
                    sample_cnt <= 0;
                    busy <= 1'b1;
                end
            end
            
            SAMPLING: begin
                busy <= 1'b1;
                
                // 总共需要接收 (mux_num + packet_header) 个数据
                if (clk_tx_negedge && sample_cnt < TOTAL_SAMPLES) begin
                    // 只有当计数值大于等于包头大小时，才写入BRAM
                    if (sample_cnt >= packet_header) begin
                        datin_bram_w_en   <= 1'b1;
                        datin_bram_w_we   <= 1'b1;
                        datin_bram_w_data <= pixel_in;
                        // BRAM地址需要减去包头的偏移量
                        datin_bram_w_addr <= sample_cnt - packet_header;
                    end
                    // 无论是否写入，采样计数器都需要增加
                    sample_cnt <= sample_cnt + 1;
                end
            end
            
            FINISH: begin
                busy <= 1'b0;
                // 检查接收到的总数据数是否正确
                if (sample_cnt != TOTAL_SAMPLES) begin
                    if (error_cnt < 255) begin
                        error_cnt <= error_cnt + 1;
                    end
                end
            end
            
            ERROR_RESET: begin
                error_rst <= 1'b1;
                error_cnt <= 0; // 错误计数器清零
            end
        endcase
    end
end

endmodule